跳到主要内容

MutationObserve 的由来

MutationObserve 提供了监视对 DOM 树所做更改的能力。它被设计为旧的 Mutation Events 功能的替代品,该功能是 DOM3 Events 规范的一部分。

Mutation Events

MutationEvents 目前已经被废弃,不再推荐此功能。虽然一些浏览器可能仍然支持它,但它已经从相关的 Web 标准中删除,所以各大浏览器正在逐步删除,或者可能只是为了兼容性目的而保留。

注意:Mutation Events 的废弃并不代表 addEventListener 函数的废弃,Mutation Events 仅表示与 DOM 交互的事件,而 addEventListener 还可以监听许多其他事件,如鼠标点击,滚动等等,具体可以参考:Events - MDN

添加 Dom Muatation Listeners 对 document 进行 dom 修改时会严重降低性能,大概导致缓慢 1.5-7 倍

以下为所有的 Mutation Events

DOMAttrModified
DOMAttributeNameChanged
DOMCharacterDataModified
DOMElementNameChanged
DOMNodeInserted
DOMNodeInsertedIntoDocument
DOMNodeRemoved
DOMNodeRemovedFromDocument
DOMSubtreeModified

可以使用以下方法注册 Mutation Events 的监听函数

element.addEventListener(
"DOMNodeInserted",
function (event) {
// ...
},
false
);

解决方案

所以这时候我们为了避免性能的减缓以及MutationEvent的废弃问题,主流开发逐渐开始使用 MutationObserver 函数,其使用方式也相对之前有所改变

性能的迟缓主要来自于 Muation Event 注册后执行的回调函数属于同步执行代码,而函数内的复杂操作属于主要的执行损耗

MutatioObserve 初始化函数

调用new MutationObserver(callback)创建并返回一个 MutationObserver 实例对象

它会在指定的 DOM 需要观察的部分变动时触发回调函数。

实例对象的方法

disconnect()

阻止 MutationObserver 实例继续接收通知,直到再次调用其observe()方法,回调函数都不会再被调用。

observe()

调用该函数会监听 DOM 更改,并匹配给定选项时,触发回调函数接收到相应通知。

调用方式为mutationObserver.observe(target[, options])

target

要观察的 DOM 节点

options

描述 DOM 的哪些变化触发 callback 回调函数,childListattributescharacterData 中,必须有一个参数为 true

options 属性方法含义
subtree为 true 时,监听以 target 为根节点的整个子树。
childList为 true 时,监听 target 节点的子级节点的新增与删除
如果subtree为 true,则监听整个子树节点的新增与删除
attributes为 true 时,监听 target 监听节点属性值的变化,默认为 true
当声明了 attributeFilter / attributeOldValue,默认为 false
attributeFilter声明哪些属性名会被监听的数组,不声明则表示全部
attributeOldValue为 true 时,记录上一次被监听的节点的属性变化
characterData为 true 时,监听节点上所有字符的变化,默认为 true。
声明了 characterDataOldValue,默认值为 false
characterDataOldValue为 true 时,记录前一个被监听的节点中发生的文本变化。默认值为 false

关于 options 与 Mutation Event 的关系

可以参考如下附表,来源于了解 HTML5 中的 MutationObserver

MutationEventMutationObserver options
DOMNodeInserted{ childList: true, subtree: true }
DOMNodeRemoved{ childList: true, subtree: true }
DOMSubtreeModified{ childList: true, subtree: true }
DOMAttrModified{ attributes: true, subtree: true }
DOMCharacterDataModified{ characterData: true, subtree: true }

takeRecords()

从 MutationObserver 的通知队列中删除所有回调函数未处理的记录,并将它们返回到新 Array 中。

此方法最常见的使用场景是在断开观察者之前立即获取所有未处理的更改记录,以便在停止观察者时可以处理任何未处理的更改。

回调函数的参数

我们在var observer = new MutationObserver(callback);中的 callback 回调函数,每当被指定的节点或子树根据配置项进行监听并触发变动时就会被调用。

回调函数拥有两个参数:

1.是描述所有被触发改动的 MutationRecord 对象数组

2.调用该函数的 MutationObserver 对象

MutationObserver 就是我们 new 出来的这个对象,那我们着重了解 MutationRecord 对象数组

MutationRecord

MutationRecord 代表一次事件触发的对象,每次发生一次变化,都会生成一个 MutationRecord 对象,最后当触发 callback 回调函数时,全部对象排列成一个 MutationRecord 数组传给回调函数。

MutationRecord 对象有许多属性,列表如下

MutationRecord 属性方法含义
type属性变化为 attributes,文字变化为 characterData,子节点变化为 childList
targetattributes 变化,返回属性变化的节点
characterData 变化,返回文字节点
childList 变化,返回子节点变化的节点。
addedNodes返回被添加的节点,默认为空数组
addedNodes 是一个数组因为某些 API 会一次性添加多个 DOM 元素
removedNodes属返回被移除的节点,默认为空数组
removedNodes 是一个数组因为某些 API 会一次性删除多个 DOM 元素
previousSibling返回被添加或移除的节点之前的兄弟节点,或者 null
nextSibling返回被添加或移除的节点之后的兄弟节点,或者 null
attributeName返回被修改的属性的属性名,或者 null
attributeNamespace返回被修改属性的命名空间(用于 XML),或者 null
例如:使用 setAttributeNS 函数设置属性时会显示对应的命名空间
oldValue对于属性 attributes 变化,返回变化之前的属性值
对于 characterData 变化返回变化之前的数据
对于子节点树 childList 变化,返回 null。
备注:attributeOldValue 或 characterDataOldValue 必须设置为 true

例子

例子已进行汉化,原出处: DOM MutationObserver

const targetNode = document.getElementById("some-id");

// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };
//subtree 监听包括target的整个子树
//childList 监听子节点的新增与删除变化
//attributes 监听节点的属性变化

// 当观察到变动时执行的回调函数
const callback = function (mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === "childList") {
console.log("节点被增加或删除");
} else if (mutation.type === "attributes") {
console.log("名为:" + mutation.attributeName + "的属性被修改");
}
}
};

// 创建一个观察器实例并传入回调函数
// observe函数会对传入的节点以及所需观察的配置项进行观察
// 发生改变则回调callback函数
const observer = new MutationObserver(callback);

// 以上述配置开始观察目标节点
observer.observe(targetNode, config);

// 可停止观察
observer.disconnect();